﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data.Common;
using System.Collections;
using System.Collections.Specialized;

namespace Mind.Data
{
    public static class WrapperCache
    {
        static Dictionary<Type, Dictionary<object, IWrapper>> _Cache;
        static Dictionary<Type, Dictionary<object, IWrapper>> Cache
        {
            get
            {
                if (_Cache == null)
                    _Cache = new Dictionary<Type, Dictionary<object, IWrapper>>();
                return _Cache;
            }
        }

        static Dictionary<object, IWrapper> GetWrapperCache(Type wrapperType)
        {
            Dictionary<object, IWrapper> result = null;
            if (Cache.ContainsKey(wrapperType))
                result = Cache[wrapperType];
            else
            {
                result = new Dictionary<object, IWrapper>();
                Cache[wrapperType] = result;
            }

            return result;
        }

        public static TWrapper GetById<TWrapper>(object id) where TWrapper : class, IWrapper, new()
        {
            return GetById<TWrapper>(id, false);
        }

        public static TWrapper GetById<TWrapper>(object id, bool fullCache) where TWrapper: class, IWrapper, new()
        {
            TWrapper result = null;
            Dictionary<object, IWrapper> wrapperCache = GetWrapperCache(typeof(TWrapper));
            
            if (fullCache)
            {
                if (wrapperCache.Count == 0)
                {
                    result = new TWrapper();

                    foreach (TWrapper item in result.FindBySample())
                    {
                        wrapperCache[item.GetKeyValue()] = item;
                    }

                    if (wrapperCache.Count == 0)
                    {
                        wrapperCache[-1] = null;
                    }

                    result = null;
                }

                if (wrapperCache.ContainsKey(id))
                    result = wrapperCache[id] as TWrapper;

            }
            else
            {
                if (!wrapperCache.ContainsKey(id))
                {
                    result = new TWrapper();
                    if (!result.GetById())
                        result = null;

                    wrapperCache[id] = result;
                }
                else
                    result = wrapperCache[id] as TWrapper;
            }

            return result;
        }

        public static void Clear<TWrapper>() where TWrapper : class, IWrapper, new()
        {
            GetWrapperCache(typeof(TWrapper)).Clear();
        }

        public static void Clear()
        {
            foreach (Type type in Cache.Keys)
            {
                GetWrapperCache(type).Clear();
            }
        }

        public static void Set(IWrapper wrapper)
        {
            if (wrapper == null) return;
            GetWrapperCache(wrapper.GetType())[wrapper.GetKeyValue()] = wrapper;
        }

        private static TWrapper[] Find<TWrapper>(params Predicate<TWrapper>[] where) where TWrapper : class, IWrapper, new()
        {
            Dictionary<object, IWrapper>.ValueCollection valueCollection = GetWrapperCache(typeof(TWrapper)).Values;
            IWrapper[] iarray = new IWrapper[valueCollection.Count];
            TWrapper[] tarray = new TWrapper[valueCollection.Count];
            valueCollection.CopyTo(iarray,0);
            iarray.CopyTo(tarray,0);
            //TWrapper[] result = Array.ConvertAll<IWrapper, TWrapper>(
            //    iarray, a=>(TWrapper)a);
            if (where.Length > 0)
                tarray = Array.FindAll<TWrapper>(tarray, where[0]);

            return tarray;
        }

        public static TWrapper[] FindAllCached<TWrapper>() where TWrapper : class, IWrapper, new()
        {
            return Find<TWrapper>();
        }

        public static TWrapper[] FindCached<TWrapper>(Predicate<TWrapper> where) where TWrapper : class, IWrapper, new()
        {
            return Find<TWrapper>(where);
        }
        
    }
}
